﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ScreenManager.cs" company="">
//   
// </copyright>
// <summary>
//   The screen manager.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

/*
 * Author: Juan Mompean Esteban
 * Created: 26-11-2011
 */
namespace ITU_vs_Zombies.Menu
{
    using System.Collections.Generic;
    using System.Diagnostics.Contracts;
    using System.Linq;

    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Content;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Media;

    /// <summary>
    /// The screen manager.
    /// </summary>
    public class ScreenManager : DrawableGameComponent
    {
        #region Constants and Fields

        /// <summary>
        /// The input.
        /// </summary>
        private readonly InputState input = new InputState();

        /// <summary>
        /// The screens.
        /// </summary>
        private readonly List<GameScreen> screens = new List<GameScreen>();

        /// <summary>
        /// The temp screens list.
        /// </summary>
        private readonly List<GameScreen> tempScreensList = new List<GameScreen>();

        /// <summary>
        /// The font.
        /// </summary>
        private SpriteFont font;

        /// <summary>
        /// The is initialized.
        /// </summary>
        private bool isInitialized;

        /// <summary>
        /// The sprite batch.
        /// </summary>
        private SpriteBatch spriteBatch;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="ScreenManager"/> class.
        /// </summary>
        /// <param name="game">
        /// The game.
        /// </param>
        public ScreenManager(Game game)
            : base(game)
        {
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets Font.
        /// </summary>
        public SpriteFont Font
        {
            get
            {
                return this.font;
            }
        }

        /// <summary>
        /// Gets SpriteBatch.
        /// </summary>
        public SpriteBatch SpriteBatch
        {
            get
            {
                return this.spriteBatch;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// The add screen.
        /// </summary>
        /// <param name="screen">
        /// The screen.
        /// </param>
        public void AddScreen(GameScreen screen)
        {
            Contract.Requires(screen != null);
            screen.ScreenManager = this;

            if (this.isInitialized)
            {
                screen.Activate(false);
            }

            this.screens.Add(screen);
        }

        /// <summary>
        /// The draw.
        /// </summary>
        /// <param name="gameTime">
        /// The game time.
        /// </param>
        public override void Draw(GameTime gameTime)
        {
            Contract.Requires(gameTime != null);
            foreach (GameScreen screen in this.screens.Where(screen => screen.ScreenState != ScreenState.Hidden))
            {
                screen.Draw(gameTime);
            }
        }

        /// <summary>
        /// The get screens.
        /// </summary>
        /// <returns>
        /// </returns>
        public GameScreen[] GetScreens()
        {
            return this.screens.ToArray();
        }

        /// <summary>
        /// The initialize.
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();

            this.isInitialized = true;
        }

        /// <summary>
        /// The remove screen.
        /// </summary>
        /// <param name="screen">
        /// The screen.
        /// </param>
        public void RemoveScreen(GameScreen screen)
        {
            Contract.Requires(screen != null);

            if (this.isInitialized)
            {
                screen.Unload();
            }

            this.screens.Remove(screen);
            this.tempScreensList.Remove(screen);
        }

        /// <summary>
        /// The stop music.
        /// </summary>
        public void StopMusic()
        {
            MediaPlayer.Stop();
        }

        /// <summary>
        /// The update.
        /// </summary>
        /// <param name="gameTime">
        /// The game time.
        /// </param>
        public override void Update(GameTime gameTime)
        {
            Contract.Requires(gameTime != null);

            this.input.Update();

            this.tempScreensList.Clear();

            foreach (GameScreen screen in this.screens)
            {
                this.tempScreensList.Add(screen);
            }

            bool otherScreenHasFocus = !this.Game.IsActive;
            bool coveredByOtherScreen = false;

            while (this.tempScreensList.Count > 0)
            {
                GameScreen screen = this.tempScreensList[this.tempScreensList.Count - 1];

                this.tempScreensList.RemoveAt(this.tempScreensList.Count - 1);

                screen.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

                if (screen.ScreenState != ScreenState.Active)
                {
                    continue;
                }

                if (!otherScreenHasFocus)
                {
                    screen.HandleInput(gameTime, this.input);

                    otherScreenHasFocus = true;
                }

                coveredByOtherScreen = true;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// The dispose.
        /// </summary>
        /// <param name="disposing">
        /// The disposing.
        /// </param>
        protected override void Dispose(bool disposing)
        {
            if (this.spriteBatch != null)
            {
                this.spriteBatch.Dispose();
            }
        }

        /// <summary>
        /// The load content.
        /// </summary>
        protected override void LoadContent()
        {
            Contract.Requires(this.Game != null);
            Contract.Requires(this.Game.Content != null);

            ContentManager content = this.Game.Content;

            this.spriteBatch = new SpriteBatch(this.GraphicsDevice);
            this.font = content.Load<SpriteFont>(@"fonts\menufont");

            this.screens.ForEach(screen => screen.Activate(false));
        }

        /// <summary>
        /// The unload content.
        /// </summary>
        protected override void UnloadContent()
        {
            this.screens.ForEach(screen => screen.Unload());
        }

        #endregion
    }
}